﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Main
{
   class Core
   {
      // 单一表达式计算
      private double Calc(char op, double opnd1, double opnd2)
      {
         switch (op)
         {
            case '+':
               return opnd1 + opnd2;
            case '-':
               return opnd1 - opnd2;
            case '*':
               return opnd1 * opnd2;
            case '\\':
               if (opnd2 == 0)
                  throw new Exception("opnd2 cannot be 0");
               else
                  return opnd1 / opnd2;
            default:
               throw new Exception("Wrong op");
         }
      }

      // 获取优先级
      private int GetPriority(char op)
      {
         switch (op)
         {
            case '*':
            case '/':
               return 2;
            case '+':
            case '-':
               return 1;
            default:
               return 0;
         }
      }

      // 计算子表达式
      private void CalcSub(Stack<char> opStack, Stack<double> numStack)
      {
         try
         {
            // 出栈一个操作符，两个操作数
            char op = opStack.Pop();
            double opnd2 = numStack.Pop();
            double opnd1 = numStack.Pop();
            // 计算子表达式
            double res = Calc(op, opnd1, opnd2);
            // 计算结果入栈
            numStack.Push(res);
         }
         catch
         {
            throw;
         }
      }

      // 判断当前是否读取数字
      private bool IsNum(char ch)
      {
         return (ch >= '0' && ch <= '9') || (ch == '.');
      }

      // 判断当前是否读取操作符
      private bool IsOp(char ch)
      {
         return ch == '+' ||
            ch == '-' || ch == '*' || ch == '\\';
      }

      // 表达式计算核心方法
      public double CalcExpCore(string tarExp)
      {
         // 初始化操作数栈和操作符栈
         Stack<char> opStack = new Stack<char>();
         Stack<double> numStack = new Stack<double>();

         // 为tarExp增加结束标识
         tarExp += '#';

         // 读取字符串数组
         int cur = 0;
         char op = (char)0;
         string curNumStr = "";
         bool isReadNum = false;
         try
         {
            for (; cur < tarExp.Length; ++cur)
            {
               // 读取数字
               if (IsNum(tarExp[cur]))
               {
                  isReadNum = true;
                  curNumStr += tarExp[cur];
               }
               // 读取其它
               else
               {
                  // 结算数字
                  if (isReadNum)
                  {
                     isReadNum = false;
                     double curNum = Convert.ToDouble(curNumStr);
                     curNumStr = "";
                     numStack.Push(curNum);
                  }

                  // 读取结束标识，跳出
                  if (tarExp[cur] == '#')
                     break;

                  // 读取左括号，直接入操作数栈
                  if (tarExp[cur] == '(')
                  {
                     opStack.Push(tarExp[cur]);
                  }
                  // 读取操作符，判断是否入栈
                  else if (IsOp(tarExp[cur]))
                  {
                     op = tarExp[cur];
                     // 符合入栈条件入栈
                     if (opStack.Count == 0 || opStack.Peek() == '(' || GetPriority(op) > GetPriority(opStack.Peek()))
                        opStack.Push(op);
                     // 不符合，出栈操作数和操作符，并运算
                     else
                        CalcSub(opStack, numStack);
                  }
                  // 读取右括号，出栈运算至左括号
                  else if (tarExp[cur] == ')')
                  {
                     while (opStack.Peek() != '(')
                        CalcSub(opStack, numStack);
                     // 左括号出栈
                     opStack.Pop();
                  }
               }
            }
            // 出栈剩余元素，并计算
            while (opStack.Count != 0)
               CalcSub(opStack, numStack);
            if (numStack.Count == 1)
               return numStack.Peek();
            else
               throw new Exception("Calc Error");
         }
         catch
         {
            throw;
         }
      }
   }
}
